// UpdateCallback.cpp

#include "StdAfx.h"

#include "../Defs.h"
#include "Common/String/StringConvert.h"

#include "../MS/PropVariant.h"

#include "Common/Stream/FileStreams.h"

#include "UpdateCallback.h"


namespace Common
{
namespace Archive
{
CArchiveUpdateCallback::CArchiveUpdateCallback():
  Callback(0),
  ShareForWrite(false),
  StdInMode(false),
  DirItems(0),
  ArcItems(0),
  UpdatePairs(0),
  NewNames(0)
  {}


STDMETHODIMP CArchiveUpdateCallback::SetTotal(UINT64 size)
{
  try
  {
	return Callback->SetTotal(size);
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UINT64 *completeValue)
{
  try 
  {
	return Callback->SetCompleted(completeValue);
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UINT64 *inSize, const UINT64 *outSize)
{
  try
  {
	return Callback->SetRatioInfo(inSize, outSize);
  } catch(...) 
  { return E_OUTOFMEMORY; }
}


/*
STATPROPSTG kProperties[] =
{
  { NULL, kpidPath, VT_BSTR},
  { NULL, kpidIsDir, VT_BOOL},
  { NULL, kpidSize, VT_UI8},
  { NULL, kpidCTime, VT_FILETIME},
  { NULL, kpidATime, VT_FILETIME},
  { NULL, kpidMTime, VT_FILETIME},
  { NULL, kpidAttrib, VT_UI4},
  { NULL, kpidIsAnti, VT_BOOL}
};

STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
{
  return CStatPropEnumerator::CreateEnumerator(kProperties, sizeof(kProperties) / sizeof(kProperties[0]), enumerator);
}
*/

STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UINT32 index,
      INT32 *newData, INT32 *newProps, UINT32 *indexInArchive)
{
  try
  {
	  HRESULT hRes = Callback->CheckBreak();
	  if(S_OK != hRes)
		return hRes;
	  const Common::Archive::CUpdatePair2 &up = (*UpdatePairs)[index];
	  if (newData != NULL) *newData = BoolToInt(up.NewData);
	  if (newProps != NULL) *newProps = BoolToInt(up.NewProps);
	  if (indexInArchive != NULL)
	  {
		*indexInArchive = (UINT32)-1;
		if (up.ExistInArchive())
		  *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer;
	  }
	  return S_OK;
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

STDMETHODIMP CArchiveUpdateCallback::GetProperty(UINT32 index, PROPID propID, PROPVARIANT *value)
{
  try
  {
	  const Common::Archive::CUpdatePair2 &up = (*UpdatePairs)[index];
	  CComPropVariant prop;
  
	  if (propID == kpidIsAnti)
	  {
		prop = up.IsAnti;
		prop.Detach(value);
		return S_OK;
	  }

	  if (up.IsAnti)
	  {
		switch(propID)
		{
		  case kpidIsDir:
		  case kpidPath:
			break;
		  case kpidSize:
			prop = (UINT64)0;
			prop.Detach(value);
			return S_OK;
		  default:
			prop.Detach(value);
			return S_OK;
		}
	  }
  
	  if (up.ExistOnDisk())
	  {
		  const auto di = DirItems->Items[up.DirIndex];
		switch(propID)
		{
		  case kpidPath:  prop = CComBSTR(DirItems->GetLogPath(up.DirIndex)); break;
		  case kpidIsDir:  prop = di->IsDir(); break;
		  case kpidSize:  prop = di->Size; break;
		  case kpidAttrib:  prop = di->Attributes; break;
		  case kpidCTime:  prop = di->CreationTime; break;
		  case kpidATime:  prop = di->AccessTime; break;
		  case kpidMTime:  prop = di->ModificationTime; break;
		}
	  }
	  else
	  {
		if (propID == kpidPath)
		{
		  if (up.NewNameIndex >= 0)
		  {
			prop = CComBSTR((*NewNames)[up.NewNameIndex]);
			prop.Detach(value);
			return S_OK;
		  }
		}
		if (up.ExistInArchive() && Archive)
		{
		  UINT32 indexInArchive;
		  if (ArcItems == 0)
			indexInArchive = up.ArcIndex;
		  else
			indexInArchive = (*ArcItems)[up.ArcIndex].IndexInServer;
		  return Archive->GetProperty(indexInArchive, propID, value);
		}
	  }
	  prop.Detach(value);
	  return S_OK;
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

STDMETHODIMP CArchiveUpdateCallback::GetStream(UINT32 index, ISequentialInStream **inStream)
{
  try
  {
	  const Common::Archive::CUpdatePair2 &up = (*UpdatePairs)[index];
	  if (!up.NewData)
		return E_FAIL;
  
	  HRESULT hRes = Callback->CheckBreak();
	  if(S_OK != hRes)
		return hRes;
	  hRes = Callback->Finilize();
	  if(S_OK != hRes)
		return hRes;

	  if (up.IsAnti)
	  {
		return Callback->GetStream((*ArcItems)[up.ArcIndex].Name, true);
	  }
	  const auto & di = DirItems->Items[up.DirIndex];
	  hRes = Callback->GetStream(DirItems->GetLogPath(up.DirIndex), false);
	  if(S_OK != hRes)
		return hRes;
 
	  if (di->IsDir())
		return S_OK;

	  if (StdInMode)
	  {
		CComObject<CStdInFileStream> *inStreamSpec = new CComObject<CStdInFileStream>;
		inStreamSpec->FinalConstruct();
		CComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
		*inStream = inStreamLoc.Detach();
	  }
	  else
	  {
		CComObject<CInFileStream> *inStreamSpec = new CComObject<CInFileStream>;
		inStreamSpec->FinalConstruct();
		CComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
		const CString path = DirItems->GetPhyPath(up.DirIndex);
		if (!inStreamSpec->OpenShared(path, ShareForWrite))
		{
		  return Callback->OpenFileError(path, ::GetLastError());
		}
		*inStream = inStreamLoc.Detach();
	  }
	  return S_OK;
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(INT32 operationResult)
{
  try
  {
	return Callback->SetOperationResult(operationResult);
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UINT32 index, UINT64 *size)
{
  if (VolumesSizes.size() == 0)
    return S_FALSE;
  if (index >= (UINT32)VolumesSizes.size())
    index = VolumesSizes.size() - 1;
  *size = VolumesSizes[index];
  return S_OK;
}

STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UINT32 index, ISequentialOutStream **volumeStream)
{
  try
  {
	  TCHAR temp[16];
	  _ultot(index + 1, temp, 10);
	  CString res = temp;
	  while (res.GetLength() < 2)
		res = CString(_T('0')) + res;
	  CString fileName = VolName;
	  fileName += _T('.');
	  fileName += res;
	  fileName += VolExt;
	  CComObject<COutFileStream> *streamSpec = new CComObject<COutFileStream>;
	  streamSpec->FinalConstruct();
	  CComPtr<ISequentialOutStream> streamLoc(streamSpec);
	  if (!streamSpec->Create(fileName, false))
		return ::GetLastError();
	  *volumeStream = streamLoc.Detach();
	  return S_OK;
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(INT32 *passwordIsDefined, BSTR *password)
{
  try
  {
	return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password)
{
  try
  {
	return Callback->CryptoGetTextPassword(password);
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

}
}